/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package view;

import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import model.Container;
import model.Content;
import model.ContentType;
import model.Playable;
import model.Player;
import model.PlaylistRecord;
import observer.ChangeObserver;
import view.admin.AdminFrame;
import view.contents.AnimatedGifBox;
import view.contents.ContentBox;
import view.contents.ImageBox;
import config.Debug;
import config.Helper;
import config.Settings;

/**
 * 
 * @author Yang Wang <yang@digitalmenubox.com>
 */
public class PlayerCanvas extends JFrame implements ChangeObserver, WindowListener, MouseMotionListener {

	private static PlayerCanvas instance;
	private Player player;
	private PageLayout pLayout;

	private ContainerBox lastUpdatedContainerBox;
	private long lastActionTime;
	private ContentBox maintenanceImage;
	private boolean alwaysToFront = true;

	public PageLayout getpLayout() {
		return pLayout;
	}

	public void setpLayout(PageLayout pLayout) {
		this.pLayout = pLayout;
	}

	public long getLastActionTime() {
		return lastActionTime;
	}

	// precondition: none
	// postcondition: a maintenance screen is displayed that takes up the whole
	// screen
	public void setMaintenanceImageVisibility(boolean visible) {
		String imageFile = "maintenance.gif";
		boolean isAnimated = true;

		// if the dimension of the player is not set to 1080*1920, the gif image
		// will not fit in the container
		// thus we need to substitute the gif image with a static image
		
		// TODO changes this back as needed, right now, always show jpg
		if (true || Helper.scale(Settings.getPlayerWidth()) != 1080 || Helper.scale(Settings.getPlayerHeight()) != 1920) {
			imageFile = "maintenance.jpg";
			isAnimated = false;
		}

		if (this.maintenanceImage == null) {
			File maintenanceImageFile = new File(Settings.getImageDirectory(), imageFile);
			if (!maintenanceImageFile.exists()) {
				Helper.copyFromClassloaderToFileSystem(Settings.getImageDirectory() + File.separator + imageFile, "appdata" + File.separator + imageFile);
			}
			// if the screen size is not standard, we should use a static image
			// instead of the gif
			if (isAnimated) {
				this.maintenanceImage = new AnimatedGifBox(new Content(imageFile, ContentType.GIF), Helper.scale(Settings.getPlayerWidth()),
						Helper.scale(Settings.getPlayerHeight()));
				// this.maintenanceImage = new AnimatedGifBox(new
				// Content(imageFile, ContentType.GIF), 1080, 1920);
			} else {
				this.maintenanceImage = new ImageBox(new Content(imageFile, ContentType.IMAGE), Helper.scale(Settings.getPlayerWidth()), Helper.scale(Settings
						.getPlayerHeight()));
			}
		}

		if (visible) {
			// display the maintenance screen on top of everything else, (but
			// there is nothing on screen at this point)
			if (this.pLayout.getIndexOf(this.maintenanceImage) < 0) {
				this.pLayout.add(this.maintenanceImage, this.pLayout.highestLayer());
			}
			this.maintenanceImage.setVisible(visible);
			this.pLayout.revalidate();
			this.pLayout.repaint();
		} else {
			// remove the image from the screen
			this.pLayout.remove(this.maintenanceImage);
			this.maintenanceImage = null;
			this.pLayout.revalidate();
			this.pLayout.repaint();
		}

	}

	public void setLastActionTime(long lastActionTime) {
		this.lastActionTime = lastActionTime;
	}

	public static PlayerCanvas getInstance() {
		if (instance == null) {
			instance = new PlayerCanvas("Player");
		}
		return instance;
	}

	private PlayerCanvas(String title) {
		super(title);
		this.addMouseListener(new AdminFrameMouseListener(null));
		this.player = Player.getInstance();
		this.init();
	}

	public void init() {
		this.player.registerChangeNotifier(this);
		this.addMouseMotionListener(this);
		this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				config.Debug.log("Shutting Down Player.", Debug.SYSTEM_LEVEL);
				System.exit(0);
			}
		});

		// this.addWindowListener(this);
		// remove title bar
		this.setUndecorated(true);

		pLayout = new PageLayout();
		this.setContentPane(pLayout);
		if (shouldShowMaintenanceScreen()) {
			this.setMaintenanceImageVisibility(true);
		} else {
			this.setMaintenanceImageVisibility(false);
		}
		this.setAlwaysOnTop(true);
	}

	public void display() {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				PlayerCanvas.this.setVisible(true);
				PlayerCanvas.this.setBounds(0, 0, Helper.scale(config.Settings.getPlayerWidth()), Helper.scale(config.Settings.getPlayerHeight()));
			}
		});
		// this.setBounds(200, 200, 1600, 600);
	}

	// update the graphics of all the contents after the player has downloaded
	// an update from server
	// this will make changes to the player to reflect changes right away
	public void updateGraphics() {
		// re-add all the pages
		pLayout.updateContainers();
		if (shouldShowMaintenanceScreen()) {
			this.setMaintenanceImageVisibility(true);
		} else {
			this.setMaintenanceImageVisibility(false);
		}
	}

	public void displayFullScreen() {
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsDevice gs = ge.getDefaultScreenDevice();

		try {
			gs.setFullScreenWindow(this);
			DisplayMode dm = gs.getDisplayMode();
			this.setVisible(true);
		} catch (Exception e) {
			System.err.println("Exception: " + e);
			gs.setFullScreenWindow(null);
		}
	}

	// main method, for testing purpose only
	public static void main(String[] args) {
	}

	// update the PlayerCanvas based on the player's next playlist record;
	@Override
	public void update() {

		final PlaylistRecord record = player.getCurrentPlaylistRecord();
		// origintally used invokeAndWait in here..
		// removed invokeAndWait so that transition is possible

		if (record == null) {
			if (PlayerCanvas.this.lastUpdatedContainerBox != null) {
				PlayerCanvas.this.lastUpdatedContainerBox.clear();
				PlayerCanvas.this.lastUpdatedContainerBox.revalidate();
				PlayerCanvas.this.lastUpdatedContainerBox.repaint();
				return;
			}
		}
		if (pLayout == null) {
			// no such page
			System.out.println("Something is wrong");
		} else {
			/*
			 * if (PlayerCanvas.this.getContentPane().getComponentCount() > 0 &&
			 * PlayerCanvas.this.getContentPane().getComponent(0) != pLayout) {
			 * //if the component inside the content pane is not the new layout,
			 * then remove all and add the new layout
			 * PlayerCanvas.this.getContentPane().removeAll();
			 * PlayerCanvas.this.getContentPane().add(pLayout); } else if
			 * (PlayerCanvas.this.getContentPane().getComponentCount() == 0) {
			 * PlayerCanvas.this.getContentPane().add(pLayout); }
			 */
			PlayerCanvas.this.getContentPane().invalidate();
			PlayerCanvas.this.getContentPane().validate();
			ContainerBox cb = pLayout.getContainerBox(record.getContainerId());
			if (cb != null) {
				cb.setContent(record.getContent());
				PlayerCanvas.this.lastUpdatedContainerBox = cb;
			} else {
				// Cannot find a container box
				cb = new ContainerBox(new Container(record.getContainerId()));
				pLayout.add(cb);
				cb.setContent(record.getContent());
				PlayerCanvas.this.lastUpdatedContainerBox = cb;
			}

			if (shouldShowMaintenanceScreen()) {
				this.setMaintenanceImageVisibility(true);
			} else {
				this.setMaintenanceImageVisibility(false);
			}
			pLayout.revalidate();
			pLayout.repaint();
		}
	}

	public void play(Playable r) {
		if (pLayout == null) {
			// no such page
			System.out.println("Something is wrong");
		} else {
			ContainerBox cb = pLayout.getContainerBox(r.getContainerId());
			if (cb != null) {
				cb.setContent(r.getContent());
				PlayerCanvas.this.lastUpdatedContainerBox = cb;
			} else {
				// Cannot find a container box
				cb = new ContainerBox(new Container(r.getContainerId()));
				pLayout.add(cb);
				cb.setContent(r.getContent());
				PlayerCanvas.this.lastUpdatedContainerBox = cb;
			}
		}
	}

	public void play(ContentBox c, int containerId) {
		if (pLayout == null) {
			// no such page
			System.out.println("Something is wrong");
		} else {
			ContainerBox cb = pLayout.getContainerBox(containerId);
			if (cb != null) {
				cb.setContent(c);
				PlayerCanvas.this.lastUpdatedContainerBox = cb;
			} else {
				// Cannot find a container box
				cb = new ContainerBox(new Container(containerId));
				pLayout.add(cb);
				cb.setContent(c);
				PlayerCanvas.this.lastUpdatedContainerBox = cb;
			}
		}
	}

	@Override
	public void windowOpened(WindowEvent e) {
	}

	@Override
	public void windowClosing(WindowEvent e) {
		config.Debug.log("Shutting Down Player.", Debug.SYSTEM_LEVEL);
		System.exit(0);
	}

	@Override
	public void windowClosed(WindowEvent e) {
	}

	@Override
	public void windowIconified(WindowEvent e) {
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
	}

	@Override
	public void windowActivated(WindowEvent e) {
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
		this.pinToFront();
	}

	public void pinToFront() {
		if (this.getAlwaysToFront()) {
			Thread t = new Thread() {

				@Override
				public void run() {
					try {
						Thread.sleep(1000 * 60 * 5);
						PlayerCanvas.this.setAlwaysOnTop(true);
						PlayerCanvas.this.toFront();
					} catch (InterruptedException ex) {
						Logger.getLogger(PlayerCanvas.class.getName()).log(Level.SEVERE, null, ex);
					}
				}
			};
			t.start();
		}
	}

	public boolean noActivityInOneMinute() {
		return (this.lastActionTime + 1000 * 60 < System.currentTimeMillis() && this.lastActionTime != 0);
	}

	@Override
	public void mouseDragged(MouseEvent me) {
	}

	@Override
	public void mouseMoved(MouseEvent me) {
		this.setLastActionTime(System.currentTimeMillis());
	}

	public boolean shouldShowMaintenanceScreen() {
		if (this.pLayout.getComponents().length == 0) {
			return true;
		} else {
			// We should also display maintenance screen when all the containers
			// on player canvas is empty
			for (ContainerBox c : this.pLayout.getContainerBoxes()) {
				if (c.hasValidContent()) {
					return false;
				}
			}
		}
		return true;
	}

	public boolean getAlwaysToFront() {
		return alwaysToFront;
	}

	public void setAlwaysToFront(boolean alwaysToFront) {
		this.alwaysToFront = alwaysToFront;
	}
}

class AdminFrameMouseListener implements MouseListener {

	private AdminFrame adminFrame = null;

	public AdminFrameMouseListener(AdminFrame f) {
		this.adminFrame = f;
	}

	@Override
	public void mousePressed(MouseEvent e) {
		// throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		// throw new UnsupportedOperationException("Not supported yet.");
		showAdminPanel();
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public void mouseExited(MouseEvent e) {
		// throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public void mouseClicked(MouseEvent e) {
	}

	private void showAdminPanel() {
		if (this.adminFrame == null) {
			this.adminFrame = AdminFrame.getInstance();
		}

		if (this.adminFrame.isVisible() == false || !this.adminFrame.isShowing()) {

		}
		Player.getInstance().setPaused(true);
		PlayerCanvas.getInstance().setAlwaysOnTop(false);
		PlayerCanvas.getInstance().setAlwaysToFront(false);
		this.adminFrame.setVisible(true);
		this.adminFrame.setAlwaysOnTop(true);
		adminFrame.toFront();
	}
}